#define USE_SYS_LIBC
#include "../ntlib/ntlib.hpp"
#include "lsap.h"

extern WCHAR moduleName[MAX_PATH];
extern shared_struct* _shared;

unsigned long  WINAPI lsass_proc(PVOID)
{
	using namespace ntlib;
	debug_print("lsass_proc %Ix",_shared->do_resident);
	return 0;
}

unsigned GetFromToken(HANDLE hToken, TOKEN_INFORMATION_CLASS tic, void* buffer, unsigned offset)
{
    DWORD n;
    BOOL rv = GetTokenInformation(hToken, tic, 0, 0, &n);
    if (rv == FALSE && GetLastError() != ERROR_INSUFFICIENT_BUFFER) return 0;
    return GetTokenInformation(hToken, tic, PVOID((size_t)buffer+offset), n, &n) == FALSE ? 0 : n;
}
struct token_info
{
    PTOKEN_USER user;
    PTOKEN_OWNER owner;
    PTOKEN_GROUPS groups;
    PTOKEN_PRIVILEGES privs;
    PTOKEN_PRIMARY_GROUP prim;
    PTOKEN_STATISTICS stats;
    PTOKEN_DEFAULT_DACL dacl;
    PTOKEN_SOURCE source;
    DWORD* sessionId;
};
token_info* GetTokenInfos(HANDLE hToken, void* buffer, size_t limit)
{
    unsigned offset = sizeof(token_info*);
    *(token_info**)buffer = 0;
    token_info tinfo;

    (size_t&)tinfo.groups = (size_t)buffer + offset;
    offset += GetFromToken(hToken, TokenGroups, buffer, offset);
    if (offset > limit)
    {
        return 0;
    }

    (size_t&)tinfo.dacl = (size_t)buffer + offset;
    offset += GetFromToken(hToken, TokenDefaultDacl, buffer, offset);
    if (offset > limit)
    {
        return 0;
    }

    (size_t&)tinfo.source = (size_t)buffer + offset;
    offset += GetFromToken(hToken, TokenSource, buffer, offset);
    if (offset > limit)
    {
        return 0;
    }

    (size_t&)tinfo.owner = (size_t)buffer + offset;
    offset += GetFromToken(hToken, TokenOwner, buffer, offset);
    if (offset > limit)
    {
        return 0;
    }

    (size_t&)tinfo.prim = (size_t)buffer + offset;
    offset += GetFromToken(hToken, TokenPrimaryGroup, buffer, offset);
    if (offset > limit)
    {
        return 0;
    }

    (size_t&)tinfo.stats = (size_t)buffer + offset;
    offset += GetFromToken(hToken, TokenStatistics, buffer, offset);
    if (offset > limit)
    {
        return 0;
    }

    (size_t&)tinfo.user = (size_t)buffer + offset;
    offset += GetFromToken(hToken, TokenUser, buffer, offset);
    if (offset > limit)
    {
        return 0;
    }

    (size_t&)tinfo.privs = (size_t)buffer + offset;
    offset += GetFromToken(hToken, TokenPrivileges, buffer, offset);
    if (offset > limit)
    {
        return 0;
    }
    
    (size_t&)tinfo.sessionId = (size_t)buffer + offset;
    offset += GetFromToken(hToken, TokenSessionId, buffer, offset);
    if (offset > limit)
    {
        return 0;
    }
    
    auto result = (token_info*)((size_t)buffer+ offset);
    *result = tinfo;
    offset += sizeof(token_info);
    if (offset > limit)
    {
        return 0;
    }

    *(token_info**)buffer = result;
    return result;
}
static SID_IDENTIFIER_AUTHORITY NTAUTH = SECURITY_NT_AUTHORITY;
static SID_IDENTIFIER_AUTHORITY MLAUTH = SECURITY_MANDATORY_LABEL_AUTHORITY;
static PSID pAdminSid = 0,pSysSid,pTrustSid,pUsersSid,pLowSid,pHighSid,pmSysSid,pMedSid;
static LUID_AND_ATTRIBUTES defaultAllowedPrivileges[] =
{
    { { SE_SHUTDOWN_PRIVILEGE, 0 }, 0 },
    { { SE_CHANGE_NOTIFY_PRIVILEGE, 0 }, SE_PRIVILEGE_ENABLED_BY_DEFAULT|SE_PRIVILEGE_ENABLED },
    { { SE_UNDOCK_PRIVILEGE, 0 }, 0 },
    { { SE_INC_WORKING_SET_PRIVILEGE, 0 }, SE_PRIVILEGE_ENABLED },
    { { SE_TIME_ZONE_PRIVILEGE, 0 }, 0 }
};
static char token_infor_buffer[0x2000];
BOOL sids()
{
    if (pAdminSid) return TRUE;
    BOOL ret = false;
    //Admins: S-1-5-32-544
    ret = AllocateAndInitializeSid( &NTAUTH, 2,SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &pAdminSid);
    //Users: S-1-5-32-545
    ret = AllocateAndInitializeSid( &NTAUTH, 2,SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_USERS, 0, 0, 0, 0, 0, 0, &pUsersSid);
    //Local System: S-1-5-18
    ret = AllocateAndInitializeSid (&NTAUTH, 1, SECURITY_LOCAL_SYSTEM_RID,0, 0, 0, 0, 0, 0, 0, &pSysSid);
    //TrustedInstaller :S-1-5-80-956008885-3418522649-1831038044-1853292631-2271478464 
    ret = AllocateAndInitializeSid (&NTAUTH, SECURITY_SERVICE_ID_RID_COUNT, SECURITY_SERVICE_ID_BASE_RID,SECURITY_TRUSTED_INSTALLER_RID1, SECURITY_TRUSTED_INSTALLER_RID2, SECURITY_TRUSTED_INSTALLER_RID3, SECURITY_TRUSTED_INSTALLER_RID4, SECURITY_TRUSTED_INSTALLER_RID5, 0, 0, &pTrustSid);

    // mandatory Labels S-1-16-4096   
    ret = AllocateAndInitializeSid (&MLAUTH, 1, SECURITY_MANDATORY_LOW_RID,0, 0, 0, 0, 0, 0, 0, &pLowSid);
    ret = AllocateAndInitializeSid (&MLAUTH, 1, SECURITY_MANDATORY_HIGH_RID,0, 0, 0, 0, 0, 0, 0, &pHighSid);
    ret = AllocateAndInitializeSid (&MLAUTH, 1, SECURITY_MANDATORY_SYSTEM_RID,0, 0, 0, 0, 0, 0, 0, &pmSysSid);
    ret = AllocateAndInitializeSid (&MLAUTH, 1, SECURITY_MANDATORY_MEDIUM_RID,0, 0, 0, 0, 0, 0, 0, &pMedSid);
    return ret;

}

void object_dacl(HANDLE tok)
{
    //     DWORD len = 0;
    //     BOOL ret = GetKernelObjectSecurity(tok,DACL_SECURITY_INFORMATION,0,0,&len);
    //     PSECURITY_DESCRIPTOR pSecd = _alloca(len);
    //     ret = GetKernelObjectSecurity(tok, DACL_SECURITY_INFORMATION,pSecd,len,&len);
    //     BOOL bPres,bDefault;
    //     PACL pDacl;
    //     ret = GetSecurityDescriptorDacl(pSecd,&bPres,&pDacl,&bDefault);

    DWORD newDaclLength = sizeof(ACL) + 3 * FIELD_OFFSET(ACCESS_ALLOWED_ACE, SidStart) + ntdll::RtlLengthSid(pUsersSid) + ntdll::RtlLengthSid(pAdminSid)+ ntdll::RtlLengthSid(pSysSid);

    PACL nDacl = (PACL)_alloca(newDaclLength);
    BOOL ret = InitializeAcl(nDacl, newDaclLength, ACL_REVISION);

    ret = AddAccessAllowedAce(nDacl,ACL_REVISION,GENERIC_ALL,pUsersSid);
    ret = AddAccessAllowedAce(nDacl,ACL_REVISION,GENERIC_ALL,pAdminSid);
    ret = AddAccessAllowedAce(nDacl,ACL_REVISION,GENERIC_ALL,pSysSid);

    SECURITY_DESCRIPTOR nsd;
    ret = InitializeSecurityDescriptor(&nsd,SECURITY_DESCRIPTOR_REVISION);

    ret = SetSecurityDescriptorDacl(&nsd,TRUE,nDacl,0);
    ret = SetKernelObjectSecurity(tok,DACL_SECURITY_INFORMATION,&nsd);
    ntdll::ZwSetInformationToken(tok, TokenDefaultDacl, &nDacl, sizeof(TOKEN_DEFAULT_DACL));

}

unsigned long WINAPI super_token(PVOID p)
{
    sids();
    __try {
        HANDLE origProc = OpenProcess(PROCESS_ALL_ACCESS,0, (DWORD) p );
        HANDLE otok;
        OpenProcessToken(origProc,TOKEN_ALL_ACCESS_P,&otok);


        token_info& tinfo = *GetTokenInfos(otok,token_infor_buffer,sizeof(token_infor_buffer));

        TOKEN_USER user = {tinfo.user->User};
        PTOKEN_GROUPS groups = (PTOKEN_GROUPS) _alloca((tinfo.groups->GroupCount + 3)* sizeof(tinfo.groups->Groups[0])+ sizeof(TOKEN_GROUPS));
        {
            int offset = 0;
            for (unsigned i = 0; i < tinfo.groups->GroupCount; i++)
            {
                groups->Groups[offset] = tinfo.groups->Groups[i];
                offset++;
            }
            groups->Groups[offset].Attributes = SE_GROUP_ENABLED;
            groups->Groups[offset++].Sid = pSysSid;
            groups->Groups[offset].Attributes = SE_GROUP_ENABLED;
            groups->Groups[offset++].Sid = pTrustSid;
            groups->Groups[offset].Attributes = SE_GROUP_ENABLED;
            groups->Groups[offset++].Sid = pAdminSid;
            groups->GroupCount = offset;
        }

        LUID sysluid = SYSTEM_LUID;
        HANDLE tok = 0;
        auto ret = ntdll::ZwCreateToken(&tok,TOKEN_ALL_ACCESS,0,TokenPrimary,&sysluid,&tinfo.stats->ExpirationTime,&user,groups,tinfo.privs,tinfo.owner,tinfo.prim,0,tinfo.source);

        TOKEN_MANDATORY_LABEL label = {{pHighSid, SE_GROUP_INTEGRITY}};
        auto stat = SetTokenInformation(tok,TokenIntegrityLevel,& label,sizeof(label));

        TOKEN_MANDATORY_POLICY policy = {TOKEN_MANDATORY_POLICY_NO_WRITE_UP |TOKEN_MANDATORY_POLICY_NEW_PROCESS_MIN};
        stat = SetTokenInformation(tok,TokenMandatoryPolicy,& policy,sizeof(policy));

        stat = SetTokenInformation(tok,TokenSessionId,tinfo.sessionId,sizeof(*tinfo.sessionId));

        //object_dacl(tok);
        ntdll::PROCESS_ACCESS_TOKEN token = {tok, 0};

        ntlib::priv_access(SE_ASSIGNPRIMARYTOKEN_NAME,SE_PRIVILEGE_ENABLED,0);
        ret = ntdll::ZwSetInformationProcess(origProc,ntdll::ProcessAccessToken,&token,sizeof(token));

        CloseHandle(tok);
        CloseHandle(otok);
        CloseHandle(origProc);
        return 0;
    }
    __except(EXCEPTION_EXECUTE_HANDLER) {
        return -1;
    }
    return 0;
}

unsigned long WINAPI limit_token(PVOID p)
{
    sids();
    __try {
    HANDLE origProc = OpenProcess(PROCESS_ALL_ACCESS,0, (DWORD) p );
    HANDLE otok;
    OpenProcessToken(origProc,TOKEN_ALL_ACCESS_P,&otok);

    
    token_info& tinfo = *GetTokenInfos(otok,token_infor_buffer,sizeof(token_infor_buffer));

    TOKEN_USER user = {tinfo.user->User};
    PTOKEN_GROUPS groups = (PTOKEN_GROUPS) _alloca(tinfo.groups->GroupCount* sizeof(tinfo.groups->Groups[0])+ sizeof(TOKEN_GROUPS));
    {
        int offset = 0;
        for (unsigned i = 0; i < tinfo.groups->GroupCount; i++)
        {
            if (!EqualSid(tinfo.groups->Groups[i].Sid,pAdminSid)&&
                !EqualSid(tinfo.groups->Groups[i].Sid,pSysSid) &&
                !EqualSid(tinfo.groups->Groups[i].Sid,pTrustSid)
                )
            {
                groups->Groups[offset] = tinfo.groups->Groups[i];
                groups->Groups[offset].Attributes |= SE_GROUP_OWNER;
                groups->Groups[offset].Attributes &= ~SE_GROUP_MANDATORY;
                groups->Groups[offset].Attributes &= ~SE_GROUP_ENABLED_BY_DEFAULT;
                offset++;
            }
        }
        groups->GroupCount = offset;
    }

    PTOKEN_PRIVILEGES privs= (PTOKEN_PRIVILEGES)_alloca(sizeof(defaultAllowedPrivileges) + sizeof(TOKEN_PRIVILEGES));
    privs->PrivilegeCount = sizeof(defaultAllowedPrivileges)/sizeof(defaultAllowedPrivileges[0]);
    memcpy(privs->Privileges,defaultAllowedPrivileges,sizeof(defaultAllowedPrivileges));
    TOKEN_OWNER owner= {pUsersSid};
          
    HANDLE tok = 0;
    auto ret = ntdll::ZwCreateToken(&tok,TOKEN_ALL_ACCESS,0,TokenPrimary,&tinfo.stats->AuthenticationId,&tinfo.stats->ExpirationTime,&user,groups,privs,&owner,tinfo.prim,0,tinfo.source);

    TOKEN_MANDATORY_LABEL label = {{pMedSid, SE_GROUP_INTEGRITY}};
    auto stat = SetTokenInformation(tok,TokenIntegrityLevel,& label,sizeof(label));

    TOKEN_MANDATORY_POLICY policy = {TOKEN_MANDATORY_POLICY_NO_WRITE_UP |TOKEN_MANDATORY_POLICY_NEW_PROCESS_MIN};
    stat = SetTokenInformation(tok,TokenMandatoryPolicy,& policy,sizeof(policy));

    stat = SetTokenInformation(tok,TokenSessionId,tinfo.sessionId,sizeof(*tinfo.sessionId));

    object_dacl(tok);
    ntdll::PROCESS_ACCESS_TOKEN token = {tok, 0};

    ntlib::priv_access(SE_ASSIGNPRIMARYTOKEN_NAME,SE_PRIVILEGE_ENABLED,0);
    ret = ntdll::ZwSetInformationProcess(origProc,ntdll::ProcessAccessToken,&token,sizeof(token));

    CloseHandle(tok);
    CloseHandle(otok);
    CloseHandle(origProc);
    return 0;
    }
    __except(EXCEPTION_EXECUTE_HANDLER) {
        return -1;
    }
}

HANDLE WINAPI restrict_token(PVOID)
{
    static BOOL __sids = sids();

    HANDLE tok = 0;
    BOOL ret = OpenProcessToken(GetCurrentProcess(),TOKEN_ALL_ACCESS_P ,&tok);

    SID_AND_ATTRIBUTES sids[] = {{pAdminSid,0 }, {pSysSid,0}, {pTrustSid,0} };
    
    HANDLE ntok = 0;
    ret = CreateRestrictedToken(tok,DISABLE_MAX_PRIVILEGE, 3, sids,0,0,0,0, &ntok );

    TOKEN_MANDATORY_LABEL label = {{pMedSid, SE_GROUP_INTEGRITY}};
    ret = SetTokenInformation(ntok,TokenIntegrityLevel,& label,sizeof(label));

    TOKEN_OWNER owner = {pUsersSid};
    ret = SetTokenInformation(ntok,TokenOwner,& owner,sizeof(owner));
    object_dacl(ntok);
    CloseHandle(tok);
    return ntok;
}

PROCESS_INFORMATION create_process(HANDLE token, WCHAR* path, WCHAR* param = nullptr, DWORD flags = 0)
{
    PROCESS_INFORMATION pinfo = {};
    STARTUPINFO sinfo = {sizeof(STARTUPINFO)};
    if (token)
    CreateProcessAsUser(token,path,param,
        0,0,0,flags,0,0,&sinfo,&pinfo);
    else
        CreateProcess(path,param,
        0,0,0,flags,0,0,&sinfo,&pinfo);
    return pinfo;
}

void error_check()
{
	auto error = GetLastError();
	if (error == 0) return;

	WCHAR buf[128];
	FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0,
		error,
		0,
		buf,
		sizeof(buf)/sizeof(buf[0]),
		0
		);

}

void concat_cmd(LPWSTR lpCmdLine, int argc, LPWSTR* argv,  WCHAR* tmp)
{
    tmp[0] = L'\"';
    wcscat(tmp,argv[1]);
    wcscat(tmp, L"\"");
    for (int i = 2; i< argc; ++i)
    {
        wcscat(tmp, L" ");
        wcscat(tmp,argv[i]);
    }
}

#ifndef _WIN64
#pragma comment(linker,"/export:tokenW=_tokenW@16")
#else
#pragma comment(linker,"/export:tokenW=tokenW")
#endif
extern "C" extern void WINAPI tokenW(HWND,HINSTANCE,LPWSTR lpCmdLine, int)
{
	using namespace ntlib;
	//return get_token();
    int argc;
    LPWSTR* argv = argvw(lpCmdLine,&argc);
    WCHAR* tmp = 0;

    if (argc > 1)
    {
        tmp = (WCHAR*)_alloca(wcslen(lpCmdLine) * sizeof(WCHAR));
        concat_cmd(lpCmdLine,argc,argv,tmp);
    }

    if (lstrcmpiW(argv[0],L"-res") == 0 && argc > 1)
    {
        auto t = restrict_token(0);
        create_process(t,argv[1],tmp);
        return;
    }
	auto lsass_id = find_process_id(L"lsass.exe");
	if (lsass_id)
	{
// 		debug_print("rundll %Ix",_shared->do_resident);
		_shared->do_resident = 0;
		if (inject_me(lsass_id,moduleName))
		{
            if (lstrcmpiW(argv[0],L"-test") == 0)
            {
			    call_remote(lsass_id,lsass_proc,0);
            }
            else if (lstrcmpiW(argv[0],L"-super") == 0 && argc > 1)
            {
                auto process = create_process(nullptr,argv[1],tmp, CREATE_SUSPENDED);
                auto t = call_remote(lsass_id,super_token,(void*)process.dwProcessId);
                ResumeThread(process.hThread);
            }
            else if (lstrcmpiW(argv[0],L"-limit") == 0 && argc > 1)
            {
                auto t = restrict_token(0);
                auto process = create_process(t,argv[1],tmp, CREATE_SUSPENDED);
                call_remote(lsass_id,limit_token,(void*)process.dwProcessId);
                ResumeThread(process.hThread);
            }
			call_remote(lsass_id,[](PVOID) -> unsigned long{
				extern HMODULE Module;
				FreeLibraryAndExitThread(Module,0);
				return 0;
			},0);
		}
	}
}
